from django.shortcuts import render, redirect
from django.views import View
from django.urls import reverse
from django.contrib.auth import login
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse, JsonResponse
from django import http
# 引入正则
import re,json
# 数据库异常
from django.db import DatabaseError
# 导入响应码
from dj_shop.utils.responseCode import RESP_CODE
from dj_shop.utils.jwtGenerate import GenerateOpenidToken, TokenToJson
# 引入对象
from users.models import User
# 引入redis
from django_redis import get_redis_connection

# 引入django自带的认证信息接口authenticate,注意2
from django.contrib.auth import authenticate, logout

# 引入oauth的OauthQQUser对象
from oauth.models import OauthQQUser

import logging, re

logger = logging.getLogger('django')


# 视图：响应页面

# 注册逻辑：

class RegisterView(View):

    # 获取注册页面
    def get(self, request):
        # path=reverse('users:register')
        # print("响应注册页面",path)
        # return render(request, path)
        respMsg = request.GET.get('respMsg')
        print('respMsg: ', respMsg)
        return render(request, 'users/register.html')

    # 注册
    def post(self, request):
        '''
        前端开启csrf之后，每次请求都会携带
        csrfmiddlewaretoken: PnwMxyH2rCT2UQIvmVYaO7u48kt7p0LaKW0S5GfZoswTulf1nY7nLGPzo7tPuEVX
        默认请求类型：Content-Type: application/x-www-form-urlencoded
        用户信息：可扩展-登录平台+浏览器
        User-Agent: Mozilla/5.0 (Windows NT 10.0;
         Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
        '''
        print("请求数据：", request.POST)
        """
        <QueryDict: {
        'csrfmiddlewaretoken': ['HoMjM0iFfgL8DUlxxiHB7KVv2y4c9CLnCXgpk8QCc6oZdpS3ylQO4jg0il4UegVa'], 
        'username': ['123'], 'password': ['123456'], 'password2': ['123456'], 'mobile': ['16619954809'], 
        'image_code': [''], 'sms_code': [''], 'allow': ['on']
        }>
        """

        username = request.POST.get("username")
        password = request.POST.get("password")
        password2 = request.POST.get("password2")
        mobile = request.POST.get("mobile")
        imageCode = request.POST.get("image_code")
        smsCode = request.POST.get("sms_code")
        allow = request.POST.get("allow")

        print("验证码：", imageCode, "短信验证码：", smsCode)

        data = {
            'code': -1,
            'message': "开始注册"
        }
        # 判断所有参数是否null：all([]) 校验数组中元素是否为空，有空返回False
        if not all([username, password, password2, mobile, allow]):
            # 403权限限制
            return http.HttpResponseForbidden('缺少必传参数')

        if not re.match(r'^[a-zA-Z0-9_-]{3,20}$', username):
            return http.HttpResponseForbidden('请输入3-20个字符的用户名，且不包含中文！')
        if not re.match(r'^[a-zA-Z0-9_-]{6,20}$', password):
            return http.HttpResponseForbidden('请输入6-20个字符的密码，且不包含中文！')
        if password != password2:
            return http.HttpResponseForbidden('两次输入的密码不一致！')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('手机号码不正确！')

        # 判断短信验证码
        redisCon = get_redis_connection('verification_code')
        smsCodeServer = ""
        if redisCon.get('sms_code_%s' % mobile):
            # bytes类型需要转码
            try:
                smsCodeServer = redisCon.get('sms_code_%s' % mobile).decode()
            except TypeError as e:
                print("TypeError转换异常：", str(e))
        print("服务端短信验证码：", smsCodeServer)
        if smsCodeServer is None or smsCodeServer == "":
            data['code'] = RESP_CODE.IMAGECODEDEAD
            data['errorShortResponse'] = "短信验证码已过期！"
            return render(request, 'users/register.html', data)
        elif smsCodeServer != '' and smsCodeServer != smsCode:
            data['code'] = RESP_CODE.IMAGECODEDEAD
            data['errorShortResponse'] = "短信验证码输入错误！"
            return render(request, 'users/register.html', data)

        if allow != 'on':
            return http.HttpResponseForbidden('请阅读并同意协议！')

        try:
            print("开始保存", username, password, mobile)
            # 保存数据，自带的create_obj方法，密码自动加密，返回一个对象
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
            print("返回的user：", user)
        except DatabaseError as e:
            ex = str(e)
            print("保存失败：", DatabaseError, e, type(e))
            print("ex: ", ex, type(ex))
            # 保存失败： <class 'django.db.utils.DatabaseError'> (1062, "Duplicate entry '16619954809' for key 'mobile'")
            # 响应信息为html
            respData = {
                "respMsg": "数据错误，注册失败！"
            }
            print("reverse('users:register'): ", reverse('users:register'))
            if ex.find('mobile') != -1:
                respData['errorMMessage'] = "手机号已存在！"
                print("1", respData)
                return render(request, 'users/register.html', respData)
                # return http.HttpResponseForbidden(respData)

            if ex.find('username') != -1:
                respData['errorNMessage'] = "用户名已存在！"
                print("2", respData)
                return render(request, 'users/register.html', respData)
                # return http.HttpResponseForbidden(respData)
            else:
                print("3", respData)
                return render(request, 'users/register.html', respData)

        # django自带login方法，放入浏览器cookie和服务端session数据
        login(request, user)
        # 重定向到首页
        # return http.HttpResponse("注册成功，返回首页！")
        # 直接跳转
        # return redirect('')

        '''
        命名空间跳转 ==> 总路由命名空间contents:子路由命名空间index
        总路由：url(r'^',include(('contents.urls','contents'),namespace='contents'))
        子路由：url(r'^$',views.IndexView.as_view(),name='index')
        '''
        print("开始跳转，需要存cookie信息")
        response = redirect(reverse('contents:index'))
        response.set_cookie('username', user.username, max_age=3600 * 12 * 14)
        return response

    # 修改
    def put(self, request):
        print("修改用户")

    # 删除
    def delete(self, request):
        print("删除用户")


# 用户名重复
class UsernameCountView(View):
    def get(self, request, username):
        # filter返回数组，.count() 返回数量
        count = User.objects.filter(username=username).count()
        print("获取用户名:", username, count)
        respData = {
            'code': RESP_CODE.OK,
            'respMsg': '成功',
            'count': count
        }
        # return http.JsonResponse(respData)
        return JsonResponse(respData)


# 手机号重复
class MobileCountView(View):
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        print("获取手机号：", mobile, count)
        respData = {
            'code': RESP_CODE.OK,
            'respMsg': '成功',
            'count': count
        }
        return JsonResponse(respData)


# 用户登录页面
class LoginView(View):
    def get(self, request):
        reqMsg = request.GET.get('reqMsg')
        print('获取登录页信息', reqMsg)
        return render(request, 'users/login.html')

    def post(self, request):
        print('提交登录', request)  # <WSGIRequest: POST '/login/'>
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')
        print('用户名：', username, '密码：', password, "记住：", remembered)

        if not all([username, password]):
            return http.HttpResponseForbidden('缺少必传参数')

        if not re.match(r'^[a-zA-Z0-9_-]{3,20}$', username):
            return http.HttpResponseForbidden('请输入正确的用户名或手机号')
        if not re.match(r'^[0-9a-zA-Z]{6,20}$', password):
            return http.HttpResponseForbidden('请输入6-20个字符的英文/数字密码')

        # 通过username查询
        user = authenticate(username=username, password=password)

        respData = {
            'code': RESP_CODE.OK,
            'respMsg': '成功'
        }

        if user is None:
            # 响应html，将错误信息渲染到html
            respData['code'] = RESP_CODE.USERERR
            respData['respMsg'] = '用户名或密码错误~！'
            return render(request, 'users/login.html', respData)

        # 更新登录状态
        login(request, user)
        # 如果remembered记住密码，计入缓存
        if remembered == 'on':
            # 记住密码，保存2周时间（django默认保存2周时间1209596），单位秒
            '''
            request.session.set_expiry(value)    
            * 如果value是个整数，session会在些秒数后失效。    
            * 如果value是个datatime或timedelta，session就会在这个时间后失效。    
            * 如果value是0,用户关闭浏览器session就会失效。   
            * 如果value是None,session会依赖全局session失效策略。
            注意这里设置set_expiry失效了，还是默认的14天
            '''
            request.session.set_expiry(60)
        else:
            # 不记住密码，状态保持浏览器销毁的时候
            request.session.set_expiry(0)

        # 1.重定向到首页，直接跳转首页
        # return redirect(reverse('contents:index'))

        # 2.添加响应内容，redirect就是一个response（HttpResponse）
        # response = redirect(reverse('contents:index'))

        # 使用LoginRequiredView后，url中存在next，如果存在，则重定向到next

        next = request.GET.get('next')
        if next:
            response = redirect(next)
        else:
            response = redirect(reverse('contents:index'))

        # 将用户信息放入cookie，过期时间为14天
        response.set_cookie('username', user.username, max_age=3600 * 12 * 14)

        return response

    # 用户退出登录


class LoginOutView(View):

    def get(self, request):
        print('退出登录：', request)

        # 清除session状态
        logout(request)

        response = redirect(reverse('contents:index'))

        # 清除cookie
        response.delete_cookie('username')
        # 清除redis缓存

        # 响应结果，重定向首页
        return response


# 获取用户信息
class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):
        print('获取用户信息：', request, request.user.is_authenticated)

        # 配置是否登录的不同跳转url，这里不配置，默认dev.py中配置了LOGIN_URL
        # login_url = '/login/'
        # redirect_field_name = 'users/userInfo.html'

        return render(request, 'users/userInfo.html')


# 绑定QQ用户页面
class BindQQView(View):
    def get(self, request):
        print('前往绑定QQ页面')

        # 默认进入页面后，先给一个已存储的openid
        userid = 16619954800
        openid = 123456789

        token = GenerateOpenidToken(userid, openid)
        deToken = TokenToJson(token)

        context = {'openid': token}

        return render(request, 'users/oauth_callback.html', context)

    def post(self, request):
        print('绑定QQ-userid-openid：', request)

        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        code = request.POST.get('code')
        shortMSG = request.POST.get('shortMSG')
        openid = request.POST.get('openid')

        print("len(str(openid)): ", len(str(openid)))
        # openid = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOjE2NjE5OTU0ODAwLCJvcGVuaWQiOjEyMzQ1Njc4OX0.Z3t3F5e48DGNMBIOUL9M8wJINaqGwnT8DmvjuO4rDP0'
        if len(str(openid)) < 120:
            return render(request, 'users/oauth_callback.html', {'openidErrorMsg': 'openid错误，请重新登录获取'})
        tokenToJson = TokenToJson(openid)


        # 短信验证码暂时发不出去，需要启动celery任务 shortMSG
        if not all([mobile, password, code, shortMSG, openid]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确的手机号')
        if not re.match(r'^[0-9a-zA-Z]{6,20}$', password):
            return http.HttpResponseForbidden('请输入正确的密码')

        # 判断短信验证码
        redisCon = get_redis_connection('verification_code')
        smsServer = ""

        if redisCon.get('sms_code_%s' % mobile):
            try:
                # bytes类型需要转码，decode('')默认为utf-8
                smsServer = redisCon.get('sms_code_%s' % mobile).decode()
            except TypeError as e:
                print('bytes转换异常', str(e))
        print('smsServer', smsServer)
        if smsServer is None or smsServer == '':
            return render(request, 'users/oauth_callback.html', {'errorShortResponse': '短信验证码已过期'})
        elif smsServer != '' and smsServer != shortMSG:
            return render(request, 'users/oauth_callback.html', {'errorShortResponse': '短信验证码输入错误'})

        # 判断openid是否有效：【在绑定生成openid的时候，可以做过期时间 600s有效期】
        code = RESP_CODE.OK

        if tokenToJson == '秘钥错误':
            code = RESP_CODE.ALLOWERR
            return render(request, 'users/oauth_callback.html', {'openidErrorMsg': 'openid授权失败，请重新登录获取'})

        # 用户是否已存在
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 1.不存在
            # 创建用户
            print("新建用户：", mobile)
            user=User.objects.create_user(username=mobile, password=password, mobile=mobile)

        else:
            # 2.已存在
            # 校验密码
            print("校验密码：", user.check_password(password))
            if not user.check_password(password):
                return render(request, 'users/oauth_callback.html', {'accountErrorMsg': '账号或密码错误'})

        # 将（新建/已存在的用户）userid绑定openid到dj_user_oauth_qq表
        # 两步存qqUser
        # qqUser= OauthQQUser(user=user, openid=openid)
        # qqUser.save()

        # 需要json转换 <class 'authlib.jose.rfc7519.claims.JWTClaims'>
        print("tokenToJson: ", tokenToJson, type(tokenToJson))
        openToken = json.loads(json.dumps(tokenToJson))
        print("openToken: ", openToken, type(openToken))
        print("openToken字段：",openToken['openid'])

        # 一步存qqUser
        try:
            # 如果是qq给的可以直接存，如果是自定义的，不能直接存openid（通过加密的太长了，这里需要jwt解析的openid）
            qqUser = OauthQQUser.objects.create(user=user, openid=openToken['openid'])
        except Exception as e:
            logger.error(e)
            return render(request, 'users/oauth_callback.html', {'accountErrorMsg': '保存授权信息失败，请联系后台客服'})
        # 更新登录状态
        login(request, qqUser.user)
        # 重定向到首页，如果存在state，则到下一页
        nex = request.GET.get('next')
        if nex is not None:
            response = redirect(nex)
        else:
            response = redirect(reverse('contents:index'))
        # coolie更新用户名username
        response.set_cookie('username', qqUser.user.username, max_age=3600 * 24 * 7)

        # 执行响应
        return response
